/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import crypto from 'crypto';

import { DirectorySnapshot } from '../core/directory-snapshot.js';
import { FileSnapshot } from '../core/file-snapshot.js';
import { BasicFileSnapshot } from '../core/filesystem-snapshot.js';
import { FileMetaData } from '../util/file-metadata.js';
import { FileType } from '../util/file-type.js';
import { Hash, HashAlgorithm } from '../util/hash.js';

import { FsEvent } from './fs-event.js';
import { FsSnapshotBuilder } from './fs-snapshot-builder.js';

/**
 * 快照生成器
 * 仅hash文件信息, 未hash文件内容
 *
 * @since 2022/08/27
 */
export class GenericFsSnapshotBuilder implements FsSnapshotBuilder {
  private directoryStack: Array<DirectorySnapshot> = [];
  private directorySnapshot: BasicFileSnapshot | undefined;

  enterDirectory(ev: FsEvent): void {
    const snapshot = this.assembleSnapshot(ev, true) as DirectorySnapshot;
    this.directoryStack.push(snapshot);
  }

  visitEntry(ev: FsEvent): void {
    const snapshot = this.assembleSnapshot(ev, false) as FileSnapshot;
    this.hashItem(snapshot);
    this.handleEntry(snapshot);
  }

  leaveDirectory(ev: FsEvent): void {
    const completeFolder = this.directoryStack.pop();
    if (completeFolder) {
      this.completeDirectory(completeFolder);
      this.handleEntry(completeFolder);
    }
  }

  handleEntry(snapshot: BasicFileSnapshot) {
    const last = this.peekLast();
    if (last) {
      last.children.push(snapshot);
    } else {
      this.directorySnapshot = snapshot;
    }
  }

  private completeDirectory(directory: DirectorySnapshot): void {
    const hash = crypto.createHash(HashAlgorithm.MD5).update(directory.name);
    directory.children.sort().forEach((item) => hash.update(item.hashValue).update(item.name));
    directory.hashValue = hash.digest('hex');
  }

  hashItem(snapshot: FileSnapshot): string {
    return Hash.hash(`${snapshot.name}${snapshot.type}${snapshot.fileMetaData.lastModifiedTime}${snapshot.fileMetaData.size}`);
  }

  getResult(): BasicFileSnapshot | undefined {
    return this.directorySnapshot;
  }

  private assembleSnapshot(ev: FsEvent, isDirectory: boolean): BasicFileSnapshot {
    if (isDirectory) {
      return new DirectorySnapshot(ev.getName(), ev.getFile(), FileType.DIRECTORY, ev.getStats().isSymbolicLink());
    }
    const metaData = new FileMetaData(ev.getStats().size, ev.getStats().mtimeMs);
    return new FileSnapshot(ev.getName(), ev.getFile(), FileType.FILE, ev.getStats().isSymbolicLink(), metaData);
  }

  private peekLast(): DirectorySnapshot | undefined {
    const length = this.directoryStack.length;
    if (length < 1) {
      return undefined;
    }
    return this.directoryStack[length - 1];
  }
}
